#include <iostream>
#include <unordered_map>
#include <list>
#include <vector>
#include <stdexcept>

class VirtualMemory {
public:
    VirtualMemory(size_t numPages, size_t pageSize);
    void accessPage(size_t virtualAddress);
    void printPageTable() const;

private:
    size_t pageSize; // Size of each page
    size_t numPages; // Number of pages in memory
    std::unordered_map<size_t, size_t> pageTable; // Maps virtual page number to physical frame
    std::list<size_t> pageList; // For LRU page replacement
    std::vector<bool> pageInMemory; // Track if a page is in memory

    void handlePageFault(size_t virtualPage);
    void loadPage(size_t virtualPage);
    void removePage(size_t virtualPage); // Function declaration

    size_t getPhysicalFrame(size_t virtualPage);
};

VirtualMemory::VirtualMemory(size_t numPages, size_t pageSize)
    : numPages(numPages), pageSize(pageSize), pageInMemory(numPages, false) {}

void VirtualMemory::accessPage(size_t virtualAddress) {
    size_t virtualPage = virtualAddress / pageSize;

    // Check if the page is already in memory
    if (pageTable.find(virtualPage) != pageTable.end()) {
        std::cout << "Accessing page: " << virtualPage << " (in memory)\n";
        // Update LRU
        pageList.remove(virtualPage);
        pageList.push_back(virtualPage);
    } else {
        std::cout << "Page fault: " << virtualPage << " (not in memory)\n";
        handlePageFault(virtualPage);
    }
}

void VirtualMemory::handlePageFault(size_t virtualPage) {
    if (pageList.size() >= numPages) {
        // Need to replace an existing page
        size_t pageToReplace = pageList.front(); // Get the oldest page
        removePage(pageToReplace);
        pageList.pop_front(); // Remove it from the LRU list
    }
    loadPage(virtualPage); // Load the new page
}

void VirtualMemory::loadPage(size_t virtualPage) {
    size_t physicalFrame = getPhysicalFrame(virtualPage);
    if (!pageInMemory[physicalFrame]) {
        pageTable[virtualPage] = physicalFrame; // Update page table
        pageInMemory[physicalFrame] = true; // Mark page as loaded
        std::cout << "Loaded page: " << virtualPage << " into frame: " << physicalFrame << "\n";
    }
    pageList.remove(virtualPage); // Remove the page from its current position in the LRU list
    pageList.push_back(virtualPage); // Add the page to the end of the LRU list
}

void VirtualMemory::removePage(size_t virtualPage) {
    // Remove the page from the page table and mark it as not in memory
    size_t physicalFrame = getPhysicalFrame(virtualPage);
    pageTable.erase(virtualPage);
    pageInMemory[physicalFrame] = false;
    std::cout << "Removed page: " << virtualPage << " into frame: " << physicalFrame << "\n";
}

size_t VirtualMemory::getPhysicalFrame(size_t virtualPage) {
    return virtualPage % numPages; // For simplicity, map directly; in real systems this would be more complex
}

void VirtualMemory::printPageTable() const {
    std::cout << "Current Page Table:\n";
    for (const auto& entry : pageTable) {
        std::cout << "Virtual Page: " << entry.first << " -> Physical Frame: " << entry.second << "\n";
    }
}

int main() {
    size_t numPages = 4; // Number of pages in memory
    size_t pageSize = 1024; // Size of each page in bytes
    VirtualMemory vm(numPages, pageSize);

    // Simulate page accesses
    std::vector<size_t> accesses = {0, 1, 2, 3, 4, 0, 5, 6, 0, 1, 2};

    for (size_t address : accesses) {
        vm.accessPage(address * pageSize);
    }

    vm.printPageTable();

    return 0;
}
